#include "../Card.h"
#include "../Logic.h"
#include "../Table.h"
#include <algorithm>
#include <assert.h>
#include <vector>
#include <iostream>
#define EVALUATE_TABLE 1
#define GET_GAME_WINNER 1
#define DO_WAR 1
#define DO_TICK 1
#define VERBOSE 0
/*
WARNING:  No checks were done to ensure drawing worked.
*/
using namespace std;
int main(void)
{
  Logic logic;
  /*
  Focus on 4-player tests... this is the main game. Add more coverage later if
  needed.
  */
  Table table(4);
  Deck* d = &table.activeCards;
  vector<Player>* p = &table.players;
  logic.setTable(&table);
  /* Get some cards to work with. */
  vector<Player*> players(100);
  for(int i = 0; i < players.size(); ++i)
    players.at(i) = new Player();
  int c = 0;
  Card ha(  hearts,   ace, c); ha.setOwner(players.at(c++));
  Card sa(  spades,   ace, c); sa.setOwner(players.at(c++));
  Card d2(diamonds,    _2, c); d2.setOwner(players.at(c++));
  Card s2(  spades,    _2, c); s2.setOwner(players.at(c++));
  Card c2(   clubs,    _2, c); c2.setOwner(players.at(c++));
  Card c3(   clubs,    _3, c); c3.setOwner(players.at(c++));
  Card h3(  hearts,    _3, c); h3.setOwner(players.at(c++));
  Card c4(   clubs,    _4, c); c4.setOwner(players.at(c++));
  Card c5(   clubs,    _5, c); c5.setOwner(players.at(c++));
  Card j0(   clubs, joker, c); j0.setOwner(players.at(c++));
  Card j1(   clubs, joker, c); j1.setOwner(players.at(c++));
  Card j2(   clubs, joker, c); j2.setOwner(players.at(c++));
  Card j3(   clubs, joker, c); j3.setOwner(players.at(c++));
  #if EVALUATE_TABLE
    vector<Player*> result;
    /* Test basic win. */
    d->add(&d2);
    d->add(&c3);
    d->add(&c4);
    d->add(&c5);
    result = logic.evaluateTable();
    assert(result.size() == 1);
    assert(find(result.begin(), result.end(), c5.getOwner()) != result.end());
    /* Test basic war. */
    table.warTime = false;
    d->replace(2, &h3);
    result = logic.evaluateTable();
    assert(result.size() == 2);
    assert(find(result.begin(), result.end(), c3.getOwner()) != result.end());
    assert(find(result.begin(), result.end(), h3.getOwner()) != result.end());
    /* Aces are low during peace. */
    table.warTime = false;
    d->replace(2, &ha);
    #if 0
      cout << "Peaceful Ace setup:";
      d->debugPrint();
    #endif
    result = logic.evaluateTable();
    assert(result.size() == 1);
    assert(find(result.begin(), result.end(), c5.getOwner()) != result.end());
    /* Aces are high during war. */
    table.warTime = true;
    result = logic.evaluateTable();
    assert(result.size() == 1);
    assert(find(result.begin(), result.end(), ha.getOwner()) != result.end());
    /* Make sure a war of aces makes all aces win. */
    table.warTime = true;
    d->replace(0, &sa);
    result = logic.evaluateTable();
    assert(result.size() == 2);
    assert(find(result.begin(), result.end(), ha.getOwner()) != result.end());
    assert(find(result.begin(), result.end(), sa.getOwner()) != result.end());
    /* In peace ace beats joker. */
    d->draw(d->size());
    d->add(&j0);
    d->add(&sa);
    table.warTime = false;
    result = logic.evaluateTable();
    assert(result.size() == 1);
    assert(find(result.begin(), result.end(), sa.getOwner()) != result.end());
    /* In war, ace beats joker. */
    d->add(&ha);
    table.warTime = true;
    result = logic.evaluateTable();
    assert(result.size() == 2);
    assert(find(result.begin(), result.end(), sa.getOwner()) != result.end());
    assert(find(result.begin(), result.end(), ha.getOwner()) != result.end());
    #if 0
      d->debugPrint();
    #endif
    #if VERBOSE
      cout << "Logic::evaluateTable() passed all tests."<< endl;
    #endif
  #endif
  #if GET_GAME_WINNER
    /* Try everyone alive. */
    p->at(0).setOwner(NULL);
    p->at(1).setOwner(NULL);
    p->at(2).setOwner(NULL);
    p->at(3).setOwner(NULL);
    assert(logic.getGameWinner() == -1);
    /* Try first dead. */
    p->at(0).setOwner(&p->at(1));
    assert(logic.getGameWinner() == -1);
    /* Try last player dead. */
    p->at(0).setOwner(NULL);
    p->at(3).setOwner(&p->at(0));
    assert(logic.getGameWinner() == -1);
    /* Try middle player dead. */
    p->at(3).setOwner(NULL);
    p->at(1).setOwner(&p->at(0));
    assert(logic.getGameWinner() == -1);
    /* Try multiple dead players. */
    p->at(0).setOwner(&p->at(2));
    p->at(1).setOwner(&p->at(2));
    assert(logic.getGameWinner() == -1);
    /* Try first player winner. */
    p->at(0).setOwner(NULL);
    p->at(1).setOwner(&p->at(0));
    p->at(2).setOwner(&p->at(0));
    p->at(3).setOwner(&p->at(0));
    assert(logic.getGameWinner() == 0);
    /* Try last player winner. */
    p->at(0).setOwner(&p->at(3));
    p->at(1).setOwner(&p->at(3));
    p->at(2).setOwner(&p->at(3));
    p->at(3).setOwner(NULL);
    assert(logic.getGameWinner() == 3);
    /* Try middle player winner. */
    p->at(0).setOwner(&p->at(2));
    p->at(1).setOwner(&p->at(2));
    p->at(2).setOwner(NULL);
    p->at(3).setOwner(&p->at(2));
    assert(logic.getGameWinner() == 2);
    /* Try everyone dead. */
    p->at(0).setOwner(&p->at(2));
    p->at(1).setOwner(&p->at(2));
    p->at(2).setOwner(&p->at(2));
    p->at(3).setOwner(&p->at(2));
    assert(logic.getGameWinner() == -2);
    #if VERBOSE
      cout << "Logic::getGameWinner() passed all tests."<< endl;
    #endif
  #endif
  #if DO_WAR
    Table warTable(4);
    logic.setTable(&warTable);
    warTable.warTime = true;
    Deck basePillagePile;
    basePillagePile.add(&ha);
    basePillagePile.add(&sa);
    basePillagePile.add(&d2);
    basePillagePile.add(&s2);
    Deck baseSpoilsPile;
    baseSpoilsPile.add(&c2);
    baseSpoilsPile.add(&c3);
    baseSpoilsPile.add(&h3);
    baseSpoilsPile.add(&c4);
    vector<Player> basePlayers = table.players;

    /* See what happens if no player has submitted. */
    warTable.activeCards                = Deck();
    warTable.pillagePile                = basePillagePile;
    warTable.spoilsPile                 = baseSpoilsPile;
    warTable.players                    = basePlayers;
    warTable.players.at(0).setWarFlag(true);
    warTable.players.at(1).setWarFlag(true);
    warTable.players.at(2).setWarFlag(true);
    warTable.players.at(3).setWarFlag(true);
    warTable.soloMover                  = &warTable.players.at(0);
    warTable.warTime                    = true;
    logic.doWar();
    assert(warTable.activeCards.size() == 0);
    assert(warTable.pillagePile.size() == 4);
    assert(warTable.spoilsPile.size() == 4);
    assert(warTable.warTime == true);
    assert(warTable.players.at(0).getWarFlag() == true);
    assert(warTable.players.at(1).getWarFlag() == true);
    assert(warTable.players.at(2).getWarFlag() == true);
    assert(warTable.players.at(3).getWarFlag() == true);
    /* See what happens if one player has submitted. */
    warTable.players.at(2).setWarFlag(false);
    logic.setActive(&c5, &warTable.players.at(2));
    logic.doWar();
    assert(warTable.activeCards.size() == 1);
    assert(warTable.pillagePile.size() == 4);
    assert(warTable.spoilsPile.size() == 4);
    assert(warTable.warTime == true);
    assert(warTable.players.at(0).getWarFlag() == true);
    assert(warTable.players.at(1).getWarFlag() == true);
    assert(warTable.players.at(2).getWarFlag() == false);
    assert(warTable.players.at(3).getWarFlag() == true);
    /* All submitted and clear winner. */
    warTable.players.at(0).setWarFlag(false);
    warTable.players.at(1).setWarFlag(false);
    warTable.players.at(2).setWarFlag(false);
    warTable.players.at(3).setWarFlag(false);
    logic.setActive(&c2, &warTable.players.at(0));
    logic.setActive(&c3, &warTable.players.at(1));
    logic.setActive(&c4, &warTable.players.at(2));
    logic.setActive(&c5, &warTable.players.at(3));
    logic.doWar();
    assert(warTable.activeCards.size() == 0); /* No active cards. */
    assert(warTable.pillagePile.size() == 0); /* Clear everything. */
    assert(warTable.spoilsPile.size() == 0);
    assert(warTable.warTime == false);
    assert(warTable.soloMover == &warTable.players.at(3)); /* Player won. */
    assert(warTable.players.at(0).getWarFlag() == false);
    assert(warTable.players.at(1).getWarFlag() == false);
    assert(warTable.players.at(2).getWarFlag() == false);
    assert(warTable.players.at(3).getWarFlag() == false);
    /* All submitted and there's a tie. */
    warTable.activeCards                = Deck(); /* Reset to old state. */
    warTable.pillagePile                = basePillagePile;
    warTable.spoilsPile                 = baseSpoilsPile;
    warTable.players                    = basePlayers;
    warTable.players.at(0).setWarFlag(false);
    warTable.players.at(1).setWarFlag(false);
    warTable.players.at(2).setWarFlag(false);
    warTable.players.at(3).setWarFlag(false);
    warTable.soloMover                  = &warTable.players.at(0);
    warTable.warTime                    = true;
    logic.setActive(&ha, &warTable.players.at(0)); /* Ace tie. */
    logic.setActive(&sa, &warTable.players.at(1));
    logic.setActive(&c4, &warTable.players.at(2));
    logic.setActive(&c5, &warTable.players.at(3));
    logic.doWar();
    assert(warTable.activeCards.size() == 4); 
    assert(warTable.pillagePile.size() == 4); 
    assert(warTable.spoilsPile.size() == 4+3+3); /* Tax. */
    assert(warTable.warTime == true);
    assert(warTable.players.at(0).getWarFlag() == true);
    assert(warTable.players.at(1).getWarFlag() == true);
    assert(warTable.players.at(2).getWarFlag() == false);
    assert(warTable.players.at(3).getWarFlag() == false);
    #if VERBOSE
      cout << "Logic::doWar() passed all tests."<< endl;
    #endif
  #endif
  #if DO_TICK
    /* Make sure nothing happens if called without a queued action. */
    Table dtTable(4);
    logic.setTable(&dtTable);
    dtTable.soloMover                  = &dtTable.players.at(0);
    int pillageCount = dtTable.pillagePile.size();
    int spoilsCount = dtTable.spoilsPile.size();
    logic.doTick();
    assert(dtTable.activeCards.size() == 0);
    assert(dtTable.pillagePile.size() == pillageCount);
    assert(dtTable.spoilsPile.size() == spoilsCount);
    assert(dtTable.soloMover == &dtTable.players.at(0));
    assert(dtTable.warTime == false);
    /* Make sure nothing happens if a player who can't move wants to move. */
    int id = 1000;
    for(int i = 0; i < 4; ++i)
    {
      Deck* hand = dtTable.players.at(i).getHand();
      hand->draw(1000);
      hand->add(new Card(diamonds,   ace, id++));
      hand->add(new Card(  hearts, joker, id++));
      hand->add(new Card(  spades,    _5, id++));
      #if 0
        cout << "Player " << i << " has:";
	hand->debugPrint();
      #endif
    }
    dtTable.players.at(1).setMoveCard(dtTable.players.at(1).getHand()->at(2));
    dtTable.players.at(2).setMoveCard(dtTable.players.at(2).getHand()->at(2));
    logic.doTick();
    assert(dtTable.activeCards.size() == 0);
    assert(dtTable.pillagePile.size() == pillageCount);
    assert(dtTable.spoilsPile.size() == spoilsCount);
    assert(dtTable.soloMover == &dtTable.players.at(0));
    assert(dtTable.warTime == false);
    /* Make sure that requests to play cards change the card in play. */
    dtTable.players.at(0).setMoveCard(dtTable.players.at(0).getHand()->at(2));
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    assert(dtTable.activeCards.size() == 3);
    assert(dtTable.pillagePile.size() == pillageCount);
    assert(dtTable.spoilsPile.size() == spoilsCount+3);
    assert(dtTable.soloMover == &dtTable.players.at(3)); /* Went around table.*/
    assert(dtTable.warTime == false);
    /* Make sure war activates correctly. Make sure control went back to P0. */
    dtTable.players.at(3).setMoveCard(dtTable.players.at(3).getHand()->at(2));
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    assert(dtTable.activeCards.size() == 4);
    assert(dtTable.pillagePile.size() == pillageCount);
    assert(dtTable.spoilsPile.size() == 1+1+1+1+3*4);
    assert(dtTable.soloMover == &dtTable.players.at(0));
    assert(dtTable.warTime == true);
    /* Make sure that jokers trigger a spoils spill. */
    dtTable.players.at(2).setMoveCard(dtTable.players.at(2).getHand()->at(1));
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    assert(dtTable.activeCards.size() == 4); /* Should have been a replace. */
    assert(dtTable.pillagePile.size() == 1); /* One card swap. */
    assert(dtTable.spoilsPile.size() == 0);
    assert(dtTable.warTime == true); /* Remove war LATER. */
    /* Finish the war with the joker. Check that 2 can't go again. Ace low*/
    dtTable.players.at(0).setMoveCard(dtTable.players.at(2).getHand()->at(0));
    dtTable.players.at(1).setMoveCard(new Card(diamonds, _8, id++));
    dtTable.players.at(2).setMoveCard(new Card(diamonds, _9, id++));
    dtTable.players.at(3).setMoveCard(new Card(diamonds, _9, id++));
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    assert(dtTable.activeCards.size() == 0); /* Dump cards post war. */
    assert(dtTable.pillagePile.size() == 0);
    assert(dtTable.spoilsPile.size() == 0);
    assert(dtTable.soloMover == &dtTable.players.at(3)); /* P3 wins. */
    assert(dtTable.warTime == false); /* Remove war NOW. */
    /* Make sure that soloMover changes when a clean battle happens. */
    dtTable.players.at(0).setMoveCard(new Card(diamonds,ace, id++));
    dtTable.players.at(1).setMoveCard(new Card(diamonds, _4, id++));
    dtTable.players.at(2).setMoveCard(new Card(diamonds, _5, id++));
    dtTable.players.at(3).setMoveCard(new Card(diamonds, _6, id++));
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    assert(dtTable.activeCards.size() == 0); /* Dump cards post war. */
    assert(dtTable.pillagePile.size() == 0);
    assert(dtTable.spoilsPile.size() == 4);
    assert(dtTable.soloMover == &dtTable.players.at(3)); /* Non-war, ace low. */
    assert(dtTable.warTime == false);
    /*
    Make sure players can come back as vassals.
    Make sure vassals ricochet around the table when the winner has no army.
    */
    dtTable.players.at(0).getArmy()->draw(100000);
    dtTable.players.at(0).getHand()->draw(100000);
    dtTable.players.at(1).getArmy()->draw(100000);
    dtTable.players.at(1).getHand()->draw(100000);
    dtTable.players.at(3).getArmy()->draw(100000); /* P3 can recieve 1 vassal.*/
    dtTable.players.at(3).getArmy()->add(new Card(clubs, _5, id++));
    dtTable.soloMover = &dtTable.players.at(3);
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    #if 0
      cout << "Players: ";
      for(int i = 0; i < dtTable.players.size(); ++i)
        cout << &dtTable.players.at(i) << " ";
      cout << endl;
    #endif
    assert(dtTable.players.at(0).getOwner() == &dtTable.players.at(3));
    assert(dtTable.players.at(1).getOwner() == &dtTable.players.at(2));
    assert(dtTable.players.at(2).getVassals().size() == 1);
    assert(dtTable.players.at(2).getVassals().at(0) == &dtTable.players.at(1));
    assert(dtTable.players.at(3).getVassals().size() == 1);
    assert(dtTable.players.at(3).getVassals().at(0) == &dtTable.players.at(0));
    /* Make sure stunned players don't do anything. */
    dtTable.warTime = false;
    dtTable.players.at(0).getHand()->draw(100000);
    dtTable.players.at(0).getArmy()->draw(100000);
    dtTable.players.at(1).getHand()->add(new Card(clubs, _2, id++));
    dtTable.players.at(2).getHand()->add(new Card(clubs, _2, id++));
    dtTable.players.at(3).getHand()->add(new Card(clubs, _2, id++));
    dtTable.players.at(0).setMoveCard(NULL);
    dtTable.players.at(1).setMoveCard(new Card(diamonds,  ace, id++));
    dtTable.players.at(2).setMoveCard(new Card(diamonds,joker, id++));
    dtTable.players.at(3).setMoveCard(new Card(diamonds,  ace, id++));
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    assert(dtTable.warTime == true);
    assert(dtTable.players.at(1).getWarFlag());
    assert(dtTable.players.at(3).getWarFlag());
    /*
    Make sure players re-enter appropriately.
    Check that vassals can get new vassals for their czar.
    */
    dtTable.players.at(0).setMoveCard(new Card(diamonds,  _5, id++));/*No move*/
    dtTable.players.at(1).setMoveCard(new Card(diamonds,  _5, id++)); /*winner*/
    dtTable.players.at(2).setMoveCard(new Card(diamonds,  _5, id++)); /* czar */
    dtTable.players.at(3).setMoveCard(new Card(diamonds,  _3, id++));
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    assert(dtTable.activeCards.size() == 0);
    assert(dtTable.pillagePile.size() == 0);
    assert(dtTable.spoilsPile.size() == 0);
    assert(dtTable.soloMover == &dtTable.players.at(1));
    assert(dtTable.warTime == false);
    assert(dtTable.players.at(0).getOwner() == &dtTable.players.at(2));
    /* Make sure players stay stunned when no player can take them it. */
    dtTable.players.at(0).getArmy()->draw(10000);
    dtTable.players.at(1).getArmy()->draw(10000);
    dtTable.players.at(2).getArmy()->draw(10000);
    dtTable.players.at(3).getArmy()->draw(10000);
    dtTable.players.at(0).getHand()->draw(10000); /* Stun P0. */
    dtTable.players.at(0).setMoveCard(NULL);
    dtTable.players.at(1).setMoveCard(NULL);
    dtTable.players.at(2).setMoveCard(NULL);
    dtTable.players.at(3).setMoveCard(NULL);
    dtTable.players.at(0).setOwner(NULL); /* Make vassal capture visible. */
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    assert(!dtTable.players.at(0).getIsVassal());
    /*
    Make sure players feed their vassals.
    (may need revision when doing multiplayer)
    */
    new (&dtTable) Table(4); /* Restart table. */
    dtTable.players.at(0).getArmy()->draw(1000000);
    dtTable.players.at(0).getHand()->draw(1000000);
    dtTable.players.at(1).getArmy()->draw(1000000);
    dtTable.players.at(1).getHand()->draw(1000000);
    dtTable.players.at(0).setHunger(2); /* Feeding time! */
    dtTable.players.at(1).setHunger(2);
    dtTable.players.at(2).setHunger(2); /* Hey!  I shouldn't feed you! */
    dtTable.players.at(3).setHunger(2); /* You either! */
    dtTable.players.at(0).setMoveCard(NULL);
    dtTable.players.at(1).setMoveCard(NULL);
    dtTable.players.at(2).setMoveCard(NULL);
    dtTable.players.at(3).setMoveCard(NULL);
    int p0BaseDeck = dtTable.players.at(0).getArmy()->size();
    int p1BaseDeck = dtTable.players.at(1).getArmy()->size();
    int p2BaseDeck = dtTable.players.at(2).getArmy()->size();
    int p3BaseDeck = dtTable.players.at(3).getArmy()->size();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();  /* Remember tax! */
    assert(3 == dtTable.players.at(0).getArmy()->size()+
                dtTable.players.at(0).getHand()->size());
    assert(3 == dtTable.players.at(1).getArmy()->size()+
                dtTable.players.at(1).getHand()->size());
    assert(p2BaseDeck-6 == dtTable.players.at(2).getArmy()->size());
    assert(p3BaseDeck   == dtTable.players.at(3).getArmy()->size());
    /* Player release. */
    dtTable.players.at(2).getArmy()->draw(1000000); /* Kill czar. */
    dtTable.players.at(2).getHand()->draw(1000000);
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    logic.doTick();
    assert(!dtTable.players.at(0).getIsVassal()); /* FREEDOM!!! */
    assert(!dtTable.players.at(1).getIsVassal());
    assert(dtTable.players.at(2).getVassals().size() == 0); /* No vassals. */
    #if VERBOSE
      cout << "Logic::doTick() passed all tests."<< endl;
    #endif
  #endif
  cout << "Logic has passed test set A." << endl;
  return 0;
}
